package com.dtflys.easyel.runtime;

import com.dtflys.easyel.utils.MetaHelper;
import com.dtflys.easyel.runtime.wrapper.NumberWrapper;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * 区间范围
 * @author gongjun[jun.gong@thebeastshop.com]
 * @since v1.0.0
 */
public class EasyElRange extends EasyElObjectSupport implements List {

    private Comparable from;
    private Comparable to;
    private Object step;
    private boolean includeFrom;
    private boolean includeTo;
    private int size = 0;

    public EasyElRange(Comparable from, Comparable to, Object step, boolean includeFrom, boolean includeTo) {
        this.from = from;
        this.to = to;
        this.includeFrom = includeFrom;
        this.includeTo = includeTo;
        if (step == null) {
            if ((from instanceof Integer || from instanceof Long)
                    && (to instanceof Integer || to instanceof Long)) {
                step = 1;
            }
            else if (from instanceof Character && to instanceof Character) {
                step = 1;
            }
            else if (from instanceof Byte && to instanceof Byte) {
                step = 1;
            }
        }
        this.step = step;
        this.size = 0;
    }


    public Comparable getFrom() {
        return from;
    }

    public Comparable getTo() {
        return to;
    }

    public Object getStep() {
        return step;
    }

    public EasyElRange step(Object step) {
        this.step = step;
        return this;
    }

    public boolean isIncludeFrom() {
        return includeFrom;
    }

    public boolean isIncludeTo() {
        return includeTo;
    }

    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(includeFrom ? '[' : '(');
        if (from != null) {
            builder.append(from);
        } else {
            builder.append(' ');
        }
        builder.append("..");
        if (to != null) {
            builder.append(to);
        } else {
            builder.append(' ');
        }
        builder.append(includeTo ? ']' : ')');
        return builder.toString();
    }

    protected int intStepValue() {
        if (step instanceof Number){
            return ((Number) step).intValue();
        }
        return 1;
    }

    protected Object increment(Object value, int i) {
        if (i <= 0) {
            return value;
        }
        if (value instanceof Integer) {
            return ((Integer) value).intValue() + i;
        }
        if (value instanceof Integer) {
            return ((Integer) value) + i;
        }
        int j = 0;
        for (; j < i; j++) {
            value = increment(value);
        }
        return value;
    }

    protected Object increment(Object value) {
        if (value instanceof NumberWrapper) {
            return  ((NumberWrapper) value).add((NumberWrapper) MetaHelper.wrap(intStepValue()));
        }
        else if (value instanceof Integer) {
            return new Integer(((Integer) value).intValue() + intStepValue());
        }
        else if (value instanceof Character) {
            return new Character((char) (((Character) value).charValue() + intStepValue()));
        }
        else if (value instanceof Integer) {
            return ((Integer) value) + ((Integer)step).intValue();
        }
        else if (value instanceof CharSequence) {
            int len = ((CharSequence) value).length();
            int pos = 1;
            int intStep = intStepValue();
            char lastC = ((CharSequence) value).charAt(len - pos);
            lastC += intStep;
            return ((CharSequence) value).subSequence(0, len - pos) + String.valueOf(lastC);
        }
        return null;
    }

    public List get(int index1, int index2) {
        return subList(index1, index2);
    }

    public Object get(int i) {
        if (i < 0) {
            i = size() + i;
        }
        if (i >= size()) {
            throw new IndexOutOfBoundsException("Index: " + i + " is too big for range: " + this);
        }
        Object value;
        value = from;
        if (value == null) {
            return null;
        }
        if (!includeFrom) {
            value = increment(value);
        }
        value = increment(value, i);
        return value;
    }

    @Override
    public Object set(int index, Object element) {
        return null;
    }

    @Override
    public void add(int index, Object element) {

    }

    @Override
    public Object remove(int index) {
        return null;
    }

    private int includeSize(int s) {
        if (includeFrom && includeTo) {
            return s + 1;
        }
        else if (!includeFrom && includeTo) {
            return s;
        }
        else if (includeFrom && !includeTo) {
            return s;
        }
        else {
            return s - 1;
        }
    }

    private BigInteger includeSize(BigDecimal s) {
        if (includeFrom && includeTo) {
            return s.add(new BigDecimal("1")).toBigInteger();
        }
        else if (!includeFrom && includeTo) {
            return s.toBigInteger();
        }
        else if (includeFrom && !includeTo) {
            return s.toBigInteger();
        }
        else {
            return s.subtract(new BigDecimal("1")).toBigInteger();
        }
    }


    public int size() {
        if (size == 0) {
            if (from == null || to == null) {
                size = -1;
            } else {
                if ((from instanceof Integer || from instanceof Long)
                        && (to instanceof Integer || to instanceof Long)) {
                    long fromNum = ((Number) from).longValue();
                    long toNum = ((Number) to).longValue();
                    size = includeSize((int) (toNum - fromNum));
                } else if (from instanceof Character && to instanceof Character) {
                    char fromNum = (Character) from;
                    char toNum = (Character) to;
                    size = includeSize(toNum - fromNum);
                } else if (from instanceof BigDecimal || to instanceof BigDecimal ||
                        from instanceof BigInteger || to instanceof BigInteger) {
                    BigDecimal fromNum = new BigDecimal("" + from);
                    BigDecimal toNum = new BigDecimal("" + to);
                    BigInteger sizeNum = toNum.subtract(fromNum).add(new BigDecimal(1.0)).toBigInteger();
                    size = includeSize(toNum.subtract(fromNum)).intValue();
                } else {
                    size = 0;
                    Comparable first = from;
                    Comparable value = from;
                    while (to.compareTo(value) >= 0) {
                        value = (Comparable) increment(value);
                        size++;
                        if (first.compareTo(value) >= 0) {
                            break;
                        }
                    }
                }
                if (!includeFrom) {
                    size--;
                }
                if (!includeTo) {
                    size--;
                }
            }
        }
        return size;
    }


    public boolean contains(Object o) {
        if (o instanceof Comparable) {
            Comparable obj = (Comparable) o;

            int fromResult;
            if (from == null) {
                fromResult = 1;
            }
            else {
                fromResult = obj.compareTo(from);
            }

            int toResult;
            if (to == null) {
                toResult = -1;
            }
            else {
                toResult = obj.compareTo(to);
            }

            if (includeFrom && includeTo) {
                return fromResult >= 0 && toResult <= 0;
            }
            else if (includeFrom && !includeTo) {
                return fromResult >= 0 && toResult < 0;
            }
            else if (!includeFrom && includeTo) {
                return fromResult > 0 && toResult <= 0;
            }
            else if (!includeFrom && !includeTo) {
                return fromResult > 0 && toResult < 0;
            }
        }
        return false;
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            private int index = 0;
            @Override
            public boolean hasNext() {
                return index < size();
            }

            @Override
            public Object next() {
                Object ret = get(index);
                index += intStepValue();
                return ret;
            }

            @Override
            public void remove() {
            }
        };
    }

    public boolean containsAll(Collection c) {
        for (Iterator iterator = c.iterator(); iterator.hasNext(); ) {
            Object item = iterator.next();
            if (!contains(item)) {
                return false;
            }
        }
        return true;
    }

    public int indexOf(Object o) {
        return -1;
    }

    @Override
    public int lastIndexOf(Object o) {
        return -1;
    }

    @Override
    public ListIterator listIterator() {
        return null;
    }

    @Override
    public ListIterator listIterator(int index) {
        return null;
    }

    public boolean isEmpty() {
        return false;
    }


    public List subList(int index1, int index2) {
        List ret = new ArrayList();
        if (index1 < 0) {
            index1 = size() + index1;
        }
        if (index2 < 0) {
            index2 = size() + index2;
        }
        if (index2 > index1) {
            index2 = Math.min(index2, size() - 1);
            for (int i = index1; i <= index2; i++) {
                ret.add(get(i));
            }
        }
        else {
            ret.add(get(index1));
        }
        return ret;
    }

    public Object[] toArray() {
        int len = size();
        Object[] rets = new Object[len];
        for (int i = 0; i < len; i++) {
            rets[i] = get(i);
        }
        return rets;
    }

    @Override
    public boolean add(Object o) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean addAll(Collection c) {
        return false;
    }

    @Override
    public boolean addAll(int index, Collection c) {
        return false;
    }

    @Override
    public void clear() {
    }

    @Override
    public boolean retainAll(Collection c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection c) {
        return false;
    }

    public Object[] toArray(Object[] a) {
        return null;
    }

    public EasyElRange and(EasyElRange right) {
        return right;
    }

    public EasyElRange or(EasyElRange right) {
        return right;
    }

    public boolean isMatch(Object obj) {
        return this.contains(obj);
    }

    @Override
    public MetaClass getMetaClass() {
        return MetaClassFactory.getMetaClass(getClass());
    }
}
